﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BulkCopy
{
    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Data.SqlClient;
    using System.Linq;
    using System.Text;
    using GBase.Data.GBaseClient;
    using BulkCopy.Internals;
    using System.Data.Common;


    public class GBaseBulkCopy
    {
        public GBaseBulkCopy(GBaseConnection connection)
        {
            this.DestinationDbConnection = connection;
            if (DestinationDbConnection.State != ConnectionState.Open)
            {
                DestinationDbConnection.Open();
            }
            ColumnMappings = new ColumnMapItemCollection();
            BulkCopyTimeout = 30;
        }

        public GBaseBulkCopy(string connectionStr)
        {
            DbProviderFactory providerFactory = DbProviderFactories.GetFactory("GBase.Data.GBaseClient");
            this.DestinationDbConnection = (GBaseConnection)providerFactory.CreateConnection();
            DestinationDbConnection.ConnectionString = connectionStr;
            DestinationDbConnection.Open(); 
            ColumnMappings = new ColumnMapItemCollection();
            BulkCopyTimeout = 30;
        }

        public void Close()
        {
            if (DestinationDbConnection != null)
            {
                DestinationDbConnection.Close();
                DestinationDbConnection.Dispose();
            }
        }

        /// <summary>
        /// Delegate to subscribe notification from assembly
        /// </summary>
        /// <param name="e">Event arguments </param>
        
        public delegate void OnBatchSizeCompletedDelegate(BatchSizeCompletedEventArgs e);
        /// <summary>
        /// All column mappings between source column and destination table columns
        /// </summary>
        public ColumnMapItemCollection ColumnMappings
        {
            get;
            set;
        }
        /// <summary>
        /// The connection that to be used while connecting destination column
        /// </summary>
        public GBaseConnection DestinationDbConnection
        {
            get;
            set;
        }

        /// <summary>
        /// The destination table name that need to be updated.
        /// </summary>
        public string DestinationTableName
        {
            get;
            set;
        }

        /// <summary>
        /// Size of the batch that need to be completed before notifying caller
        /// </summary>
        public int BatchSize
        {
            get;
            set;
        }

        public int BulkCopyTimeout
        {
            get;
            set;
        }

        /// <summary>
        /// Delegate that need to invoked once the assembly uploads the specified BatchSize
        /// </summary>
        public OnBatchSizeCompletedDelegate OnBatchSizeCompleted { get; set; }

        /// <summary>
        /// Method that uploads the data from the MySqlDataReader that contains the data.
        /// </summary>
        /// <param name="reader">Data reader that contains the source data that to be uploaded</param>
        public void Upload(GBaseDataReader reader)
        {

            if (reader.HasRows)
            {
                System.Data.DataTable table = new System.Data.DataTable();
                table.Load(reader);
                Upload(table);
            }
        }

        public void Upload(DataRow[] rows)
        {
            CommonFunctions functions = new CommonFunctions();
            string sql = "";
            int counter = 0;
            BatchSizeCompletedEventArgs eventArgs = new BatchSizeCompletedEventArgs();
            eventArgs.ErrorDataRows = new List<System.Data.DataRow>();
            DateTime limit = DateTime.Now.AddSeconds(BulkCopyTimeout);
            foreach (System.Data.DataRow item in rows)
            {
                try
                {
                    // SQL constructed.
                    // Using the destination connection Execute the statement
                    sql = functions.ConstructSql(DestinationTableName, item, ColumnMappings);
                    GBaseCommand command = new GBaseCommand(sql, DestinationDbConnection);
                    command.ExecuteNonQuery();
                }
                catch (Exception)
                {
                    eventArgs.ErrorDataRows.Add(item);
                }

                counter++;

                /*
                 * Issue 2:     Does not support Batch sizes like in SqlBulkCopy
                 * When the bulkupload code uploads the batch size that is specified by the caller, we need to notify the caller that
                 * The batch size is done uploading. This will help the caller to make their decisions.
                 */
                if (counter == BatchSize && counter > 0)
                {
                    // batch size is completed. invoke the OnBatchSizeCompletedDelegate to alert the caller
                    if (OnBatchSizeCompleted != null)
                    {
                        // create the event arguments

                        eventArgs.CompletedRows = counter.ToString();
                        // invoke the delegate
                        OnBatchSizeCompleted(eventArgs);
                    }
                    eventArgs.CompletedRows = "";
                    eventArgs.ErrorDataRows.Clear();
                    counter = 0;
                }

                if (BulkCopyTimeout != 0 && limit.CompareTo(DateTime.Now) <= 0)
                {
                    throw new Exception("Execute sql timeout!");
                }
            }

            // A final raise from the code. this is to catch the arbitary values that does not meet the batch size limit
            if (counter > 0)
            {
                // batch size is completed. invoke the OnBatchSizeCompletedDelegate to alert the caller
                if (OnBatchSizeCompleted != null)
                {
                    // create the event arguments
                    eventArgs.CompletedRows = counter.ToString();
                    // invoke the delegate
                    OnBatchSizeCompleted(eventArgs);
                }
            }
        }

        /// <summary>
        /// Method that uploads the data from the <see cref="System.Data.DataTable">DataTable</see> that contains the data.
        /// </summary>
        /// <param name="table">Data table that contains source data that to be uploaded</param>
        public void Upload(System.Data.DataTable table)
        {
            Upload(table.Select());
        }

    }
}
